package com.good.daoImp;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;

import org.hibernate.Query;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.jdbc.Work;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;

import com.good.dao.BaseDao;
import com.page.PageResults;
import com.page.RowMapper;
/**
 * baseDaoImpl
 * @author ieei
 *
 * @param <T>
 * @param <ID>
 */
@SuppressWarnings("unchecked")
public class BaseDaoImpl<T, ID extends Serializable> implements BaseDao<T, ID> {
	@Autowired
	private SessionFactory sessionFactory;

	public Session getSession() {
		//需要开启事物，才能得到CurrentSession
		return sessionFactory.getCurrentSession();
	}
	protected Class<T> entityClass;

	public BaseDaoImpl() {
		// getGenericSuperclass()获得带有泛型的父类
		// Type是 Java 编程语言中所有类型的公共高级接口。它们包括原始类型、参数化类型、数组类型、类型变量和基本类型。
		Type type = getClass().getGenericSuperclass();
		// ParameterizedType参数化类型，即泛型
		Type[] parameterizedType = ((ParameterizedType) type)
				.getActualTypeArguments();
		// getActualTypeArguments获取参数化类型的数组，泛型可能有多个
		entityClass = (Class<T>) parameterizedType[0];
		/*System.out.println("type==" + type + "|||paramType==="
				+ parameterizedType + "|||entity===" + entityClass);*/
	}

	/*
	 * protected Class getEntityClass() { if (entityClass == null) { entityClass
	 * = (Class<T>) ((ParameterizedType) getClass()
	 * .getGenericSuperclass()).getActualTypeArguments()[0]; } return
	 * entityClass; }
	 */

	@Override
	public void save(T entity) {
		Assert.notNull(entity, "entity不能为空");
		this.getSession().save(entity);
	}

	@Override
	public void saveOrUpdate(T entity) {
		Assert.notNull(entity, "entity不能为空");
		this.getSession().saveOrUpdate(entity);
	}

	@Override
	public T load(ID id) {
		Assert.notNull(id, "id不能为空");
		T load = (T) this.getSession().load(entityClass, id);
		return load;

	}

	@Override
	public T find(ID id) {
		Assert.notNull(id, "id不能为空");
		return (T) this.getSession().get(entityClass, id);
	}

	@Override
	public boolean contains(T entity) {
		if (entity == null) {
			return false;
		}
		return this.getSession().contains(entity);
	}

	@Override
	public void delete(T entity) {
		Assert.notNull(entity, "entity不能为空");
		this.getSession().delete(entity);
	}

	@Override
	public boolean deleteById(ID Id) {
		Assert.notNull(Id,"id不能为空");
		if (this.find(Id) != null) {
			this.getSession().delete(this.find(Id));
			return true;
		}
		return false;
	}

	@Override
	public void deleteAll(Collection<T> entities) {
		for (Object entity : entities) {
			this.getSession().delete(entity);
		}
	}

	@Override
	public void queryHql(String hqlString, Object... values) {
		Query query = this.getSession().createQuery(hqlString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		query.executeUpdate();
	}

	@Override
	public void querySql(String sqlString, Object... values) {
		Query query = this.getSession().createSQLQuery(sqlString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		query.executeUpdate();
	}

	
	@Override
	public T findByHQL(String hqlString, Object... values) {
		Query query = this.getSession().createQuery(hqlString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		return (T) query.uniqueResult();
	}

	@Override
	public T findBySQL(String sqlString, Object... values) {
		Query query = this.getSession().createSQLQuery(sqlString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		return (T) query.uniqueResult();
	}

	@Override
	public List<T> findListByHQL(String hqlString, Object... values) {
		Query query = this.getSession().createQuery(hqlString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		return query.list();
	}

	@Override
	public List<T> findListBySQL(String sqlString, Object... values) {
		Query query = this.getSession().createSQLQuery(sqlString)
				.addEntity(entityClass);
		for (int i = 0; values != null && i < values.length; i++) {
			query.setParameter(i, values[i]);
		}
		return query.list();
	}

	@Override
	public void refresh(T t) {
		Assert.notNull(t, "entity不能为空");
		this.getSession().refresh(t);
	}

	@Override
	public void update(T entity) {
		Assert.notNull(entity, "entity不能为空");
		this.getSession().update(entity);
	}

	@Override
	public Long findCount(String hql, Object... values) {
		Query query = this.getSession().createQuery(hql);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		return (Long) query.uniqueResult();
	}

	public List<T> findListBySql(final String sql, final RowMapper map,
			final Object... values) {
		final List<T> list = new ArrayList<T>();
		Work jdbcWork = new Work() {
			public void execute(Connection connection) throws SQLException {

				PreparedStatement ps = null;
				ResultSet rs = null;
				try {
					ps = connection.prepareStatement(sql);
					for (int i = 0; i < values.length; i++) {
						setParameter(ps, i, values[i]);

					}
					rs = ps.executeQuery();
					int index = 0;
					while (rs.next()) {
						Object obj = map.mapRow(rs, index++);
						list.add((T) obj);

					}
				} finally {
					if (rs != null) {
						rs.close();
					}
					if (ps != null) {
						ps.close();
					}
				}
			}
		};
		this.getSession().doWork(jdbcWork);
		return list;
	}

	@Override
	public PageResults<T> findPage(String hql, String countHql,
			int pageNo, int pageSize, Object... values) {
		PageResults<T> retValue = new PageResults<T>();
		Query query = this.getSession().createQuery(hql);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		int currentPage = pageNo > 1 ? pageNo : 1;
		retValue.setCurrentPage(currentPage);
		retValue.setPageSize(pageSize);
		if (countHql == null) {
			ScrollableResults results = query.scroll();
			results.last();
			retValue.setTotalCount(results.getRowNumber() + 1);
		} else {
			Long count = findCount(countHql, values);
			retValue.setTotalCount(count.intValue());
		}
		retValue.resetPageNo();
		List<T> itemList = query.setFirstResult((currentPage - 1) * pageSize)
				.setMaxResults(pageSize).list();
		if (itemList == null) {
			itemList = new ArrayList<T>();
		}
		retValue.setResults(itemList);

		return retValue;
	}

	/**
	 * 设置每行批处理参数
     * @param ps
     * @param pos ?占位符索引，从0开始
     * @param data
     * @throws SQLException
	 */
	private void setParameter(PreparedStatement ps, int pos, Object data)
			throws SQLException {
		if (data == null) {
			ps.setNull(pos + 1, Types.VARCHAR);
			return;
		}
		Class<? extends Object> dataCls = data.getClass();
		if (String.class.equals(dataCls)) {
			ps.setString(pos + 1, (String) data);
		} else if (boolean.class.equals(dataCls)) {
			ps.setBoolean(pos + 1, ((Boolean) data));
		} else if (int.class.equals(dataCls)) {
			ps.setInt(pos + 1, (Integer) data);
		} else if (double.class.equals(dataCls)) {
			ps.setDouble(pos + 1, (Double) data);
		} else if (Date.class.equals(dataCls)) {
			Date val = (Date) data;
			ps.setTimestamp(pos + 1, new Timestamp(val.getTime()));
		} else if (BigDecimal.class.equals(dataCls)) {
			ps.setBigDecimal(pos + 1, (BigDecimal) data);
		} else {
			ps.setObject(pos + 1, data);
		}
	}
}
